import { Sprite, Label, resources, ImageAsset, Texture2D, SpriteFrame, assetManager } from "cc";
import { tgxUIWaiting, tgxUIAlert, tgxSceneUtil, tgxAudioMgr } from "../../core_tgx/tgx";
import { ModuleDef } from "../../scripts/ModuleDef";
import { SceneDef, SubWorldSceneParams } from "../../scripts/SceneDef";
import { NetUtil } from "./NetUtil";
import { SubWorldConfig } from "../shared/SubWorldConfig";
import { UserInfo } from "../shared/types/UserInfo";
import { SubGameMgr } from "./SubGameMgr";
import { ModuleContext } from "../../core_tgx/base/ModuleContext";
import { UserLocalCache } from "./UserLocalCache";

export class UserMgr {
    private static _inst: UserMgr;
    public static get inst(): UserMgr {
        if (!this._inst) {
            this._inst = new UserMgr();
        }
        return this._inst;
    }

    constructor(){
        tgxAudioMgr.inst.musicVolume = UserLocalCache.inst.musicVolume;
        tgxAudioMgr.inst.soundVolume = UserLocalCache.inst.soundVolume;
    }

    private _token: string;
    private _userInfo: UserInfo = {
        uid: '',
        name: '',
        visualId: 0,
        gender: 0,
        introduction: '',
    }

    private _subWorldId: string;

    private _userInfoMap: { [key: string]: UserInfo } = {};

    public get userId(): string {
        return this._userInfo.uid;
    }

    public get token(): string {
        return this._token;
    }

    public get uid(): string {
        return this._userInfo.uid;
    }

    public get name(): string {
        return this._userInfo.name;
    }

    public get visualId(): number {
        return this._userInfo.visualId;
    }

    public get subWorldId(): string {
        return this._subWorldId;
    }

    async doLogin(account: string, password: string) {
        let ret = await NetUtil.callMasterApi('Login', { account: account, password: password });
        if (ret.isSucc) {
            this._token = ret.res.token;
            this._userInfo = ret.res.userInfo;
            this._subWorldId = ret.res.subWorldId;

            this._userInfoMap[this._userInfo.uid] = this._userInfo;
        }

        if (!ret.isSucc) {
            if (ret.err.message == 'USER_NOT_EXISTS' || ret.err.message == 'PASSWORD_WRONG') {
                tgxUIAlert.show('用户名或者密码错误！');
            }
        }
        else {
            //没有名字，表示还未创建角色，则进入角色创建流程
            if (!UserMgr.inst.name) {
                tgxUIWaiting.show('角色准备中');
                tgxSceneUtil.loadScene(SceneDef.CREATE_ROLE);
            }
            //如果角色在房间中，则进入房间
            else if (ret.res.subWorldId) {
                let ret2 = await UserMgr.inst.doEnterSubWorld(ret.res.subWorldId);
                if (!ret2.isSucc) {
                    //进入大厅
                    tgxSceneUtil.loadScene(SceneDef.LOBBY);
                }
            }
            else {
                //进入大厅
                tgxSceneUtil.loadScene(SceneDef.LOBBY);
            }
        }
        return ret;
    }

    async doCreateRole(name: string, visualId: number) {
        let ret = await NetUtil.callMasterApi('CreateRole', { token: this._token, name: name, visualId: visualId });
        if (ret.isSucc) {
            this._userInfo.name = ret.res.name;
            this._userInfo.visualId = ret.res.visualId;
            this._subWorldId = ret.res.subWorldId;
        }
        return ret;
    }

    async doEnterSubWorld(subWorldId: string, password?: string) {
        tgxUIWaiting.show('进入世界');
        let ret = await NetUtil.callMasterApi('EnterSubWorld', { token: this._token, subWorldId: subWorldId, password: password }, { timeout: 10000 });
        tgxUIWaiting.hide();

        if (ret.isSucc) {
            return await this.doEnterSubGame({
                ...ret.res,
                uid: UserMgr.inst.uid
            });
        }
        else {
            tgxUIAlert.show(ret.err.message);
        }
        return ret;
    }

    async doEnterSubGame(params: SubWorldSceneParams) {
        tgxUIWaiting.show('进入世界');
        //有名字，则进入对应场景，如果没有对应场景，则进入默认场景
        let config = SubWorldConfig.getSubWorldConfig(params.subWorldConfigId);
        if (!config) {
            tgxUIWaiting.show('未找到子世界配置');
            return;
        }

        await SubGameMgr.inst.loadSubGame(params.subWorldConfigId);
        let ret2 = await SubGameMgr.inst.getGameMgr(params.subWorldConfigId).enterWorld(params);

        tgxUIWaiting.hide();
        return ret2;
    }

    async setUserIconAndName(userId: string, sprIcon: Sprite, lblName?: Label, bundleName?: string) {
        bundleName ||= ModuleContext.getDefaultModule();
        if (!sprIcon && !lblName) {
            return;
        }
        let info = await this.rpc_GetUserInfo(userId);
        if (!info) {
            return;
        }
        if (lblName && lblName.isValid) {
            lblName.string = info.name || 'User_' + userId;
        }
        if (sprIcon && sprIcon.isValid) {
            let bundle = resources;
            if (bundleName) {
                bundle = assetManager.getBundle(bundleName);
            }
            bundle.load('icons/icon' + info.visualId, (err, data: ImageAsset) => {
                if (!sprIcon.isValid) {
                    return;
                }
                let tex = new Texture2D();
                tex.image = data;
                let spriteFrame = new SpriteFrame();
                spriteFrame.texture = tex;
                sprIcon.spriteFrame = spriteFrame;
            });
        }
    }


    async rpc_GetUserInfo(userId: string) {
        let info = this._userInfoMap[userId];
        if (info) {
            return info as UserInfo;
        }

        let ret = await NetUtil.callMasterApi('GetUserInfo', { token: this._token, uid: userId });
        if (!ret.isSucc || !ret.res.infos.length) {
            return null;
        }

        info = ret.res.infos[0];
        this._userInfoMap[info.uid] = info;

        return info;
    }

    async rpc_GetUserInfos(userIds: Array<string>) {
        let uncachedIds = [];
        for (let i = 0; i < userIds.length; ++i) {
            let userId = userIds[i];
            if (!this._userInfoMap[userId]) {
                uncachedIds.push(userId);
            }
        }
        if (uncachedIds.length == 0) {
            return this._userInfoMap;
        }

        let ret = await NetUtil.callMasterApi('GetUserInfo', { token: this._token, uids: uncachedIds });

        if (!ret.isSucc || !ret.res.infos.length) {
            return null;
        }

        for (let i = 0; i < ret.res.infos.length; ++i) {
            let info = ret.res.infos[i];
            this._userInfoMap[info.uid] = info;
        }

        return this._userInfoMap;
    }

    async rpc_QuickPlay(type: string, immediate?: boolean) {
        let ret = await NetUtil.callMasterApi("StartMatch", { token: this._token, type: type, immediate: immediate });
        return ret;
    }

    async rpc_GetTableList(type: string) {
        let ret = await NetUtil.callMasterApi('ListSubWorlds', { token: this._token, type: type });
        return ret;
    }

    async rpc_CreateSubWorld(type: string, name: string, password: string) {
        let ret = await NetUtil.callMasterApi("CreateSubWorld", { token: this._token, subWorldName: name, subWorldConfigId: type, password: password });
        return ret;
    }

    async rpc_ModifyUserInfo(gender: number | undefined, introduction: string | undefined) {
        let ret = await NetUtil.callMasterApi("ModifyUserInfo", { token: this._token, gender: gender, introduction: introduction });
        if (ret.isSucc) {
            if (ret.res.gender != undefined) {
                this._userInfo.gender = ret.res.gender;
            }
            if (ret.res.introduction != undefined) {
                this._userInfo.introduction = ret.res.introduction;
            }
        }
        return ret;
    }

    async rpc_GetAnnouncement(type: string) {
        let ret = await NetUtil.callMasterApi("GetAnnouncement", { token: this._token, type: type });
        return ret;
    }

    async rpc_GetNotice() {
        let ret = await NetUtil.callMasterApi("GetNotice", { token: this._token });
        return ret;
    }
}